package com.wg.net.server.tcp;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.wg.game.app.BaseApplication;
import com.wg.net.DisconnectReason;
import com.wg.net.NetConfig;
import com.wg.net.NettyAttributeKey;
import com.wg.net.session.ISession;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

import java.io.IOException;

/**
 * TCP 服务端处理器（处理连接生命周期、空闲超时、异常断开等逻辑）
 * <p>
 * 核心职责：
 * - 记录连接激活/失活日志（含客户端地址、连接时长）
 * - 管理会话生命周期（连接断开时清理会话）
 * - 处理空闲超时断开（区分读/写空闲）
 * - 捕获并处理通道异常（记录详细异常信息）
 *
 * @author 少爷123
 */
@ChannelHandler.Sharable
public class TcpServerHandler extends ChannelInboundHandlerAdapter {
    private static final Log log = LogFactory.get();

    // ------------------------------ 连接生命周期事件 ------------------------------

    /**
     * 通道激活（客户端连接成功）
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        String clientAddr = ctx.channel().remoteAddress().toString();
        log.info("TCP 客户端连接成功 | 客户端地址={}, 通道={}", clientAddr, ctx.channel());
    }

    /**
     * 通道失活（客户端断开连接）
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        String clientAddr = ctx.channel().remoteAddress().toString();

        ISession session = TcpSessionCache.getInstance().get(ctx.channel().id().asLongText());
        DisconnectReason disconnectReason = ctx.channel().attr(NettyAttributeKey.DISCONNECT_REASON).get();

        // 补充默认断开原因（若未显式设置）
        if (disconnectReason == null) {
            disconnectReason = DisconnectReason.Disconnect;
        }

        // 记录详细断开日志（含连接时长、断开原因）
        log.info("TCP 客户端断开连接 | 客户端地址={}, 断开原因={}",
                clientAddr,
                disconnectReason.getDesc());

        // 清理会话
        if (session != null) {
            BaseApplication.INST.onDisconnect(session, session.getUserData(), disconnectReason,
                    disconnectReason.getDesc());
            TcpSessionCache.getInstance().remove(session.getPeerId());
        }
    }

    // ------------------------------ 空闲超时处理 ------------------------------

    /**
     * 处理用户事件（重点是空闲超时）
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            // 仅处理配置的超时类型（区分读空闲/写空闲）
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            IdleState state = idleStateEvent.state();
            if (shouldHandleIdle(state)) {
                handleIdleTimeout(ctx, state);
            }
        }
        super.userEventTriggered(ctx, evt);
    }

    /**
     * 判断是否需要处理当前空闲状态（根据配置的超时类型）
     *
     * @param state 空闲状态（READER_IDLE/WRITE_IDLE/ALL_IDLE）
     */
    private boolean shouldHandleIdle(IdleState state) {
        long disconnectTimeout = NetConfig.getInstance().getDisconnectTimeout();
        if (disconnectTimeout <= 0) {
            return false; // 未配置超时，不处理
        }

        return state == IdleState.READER_IDLE;
    }

    /**
     * 处理空闲超时（设置断开原因并关闭连接）
     */
    private void handleIdleTimeout(ChannelHandlerContext ctx, IdleState state) {
        String clientAddr = ctx.channel().remoteAddress().toString();
        log.warn("TCP 连接空闲超时 | 客户端地址={}, 空闲状态={}, 超时时间={}ms",
                clientAddr, state, NetConfig.getInstance().getDisconnectTimeout());

        // 设置断开原因（仅当未设置过时）
        ctx.channel().attr(NettyAttributeKey.DISCONNECT_REASON).setIfAbsent(DisconnectReason.TimeoutDisconnect);
        ctx.close(); // 主动关闭连接
    }

    // ------------------------------ 异常处理 ------------------------------

    /**
     * 捕获通道异常（记录详细日志并清理连接）
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        String clientAddr = ctx.channel().remoteAddress().toString();
        log.error("TCP 通道发生异常 | 客户端地址={}, 通道={}", clientAddr, ctx.channel(), cause);

        // 区分异常类型处理（可选）
        if (cause instanceof IOException) {
            log.warn("TCP 连接正常关闭（IO异常） | 客户端地址={}", clientAddr);
        } else {
            log.error("TCP 连接异常关闭（非IO异常） | 客户端地址={}", clientAddr);
        }

        // 设置断开原因（仅当未设置过时）
        ctx.channel().attr(NettyAttributeKey.DISCONNECT_REASON).setIfAbsent(DisconnectReason.ExceptionDisconnect);
        ctx.close(); // 关闭异常连接
    }
}